package cn.funeralobjects.project.sshd.config;

import cn.funeralobjects.project.sshd.config.properties.SshProperties;
import cn.funeralobjects.project.sshd.server.SshApplication;
import cn.funeralobjects.project.sshd.utils.PublicKeyUtils;
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
import org.apache.sshd.common.util.OsUtils;
import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.scp.ScpCommandFactory;
import org.apache.sshd.server.shell.InteractiveProcessShellFactory;
import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.security.PublicKey;
import java.util.Collections;

/**
 * @author FuneralObjects
 * Create date: 2020/8/5 4:07 AM
 */
@Configuration
@EnableConfigurationProperties({SshProperties.class})
public class SshConfiguration {

    private final SshProperties sshProperties;

    @Autowired
    public SshConfiguration(SshProperties sshProperties) {
        this.sshProperties = sshProperties;
    }

    @SuppressWarnings("ResultOfMethodCallIgnored")
    @PostConstruct
    private void init() throws IOException {
        new File(sshProperties.getPubLocation()).mkdirs();
        new File(sshProperties.getSharedLocation()).mkdirs();
        new File(sshProperties.getHostKeyLocation()).createNewFile();
    }

    @Bean
    public SshServer sshServer() {
        SshServer sshServer = SshServer.setUpDefaultServer();
        sshServer.setPort(sshProperties.getPort());
        return sshServer;
    }

    @Bean
    public SshApplication sshApplication(SshServer sshServer) {
        SshApplication sshApplication = new SshApplication();
        sshApplication.setSshServer(sshServer);
        configureAuthentication(sshServer,sshProperties);
        configureKeyPairProvider(sshServer,sshProperties);
        configureSubsystem(sshServer,sshProperties);
        sshServer.setShellFactory(new InteractiveProcessShellFactory());
        return sshApplication;
    }


    private static void configureAuthentication(SshServer sshServer, SshProperties sshProperties){
        sshServer.setPublickeyAuthenticator((username, publicKey, serverSession) -> {
            try {
                PublicKey pub = PublicKeyUtils.readPubFile(new File(sshProperties.getPubLocation() + '/' + username  + ".pub"));
                return pub.equals(publicKey);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    private static void configureKeyPairProvider(SshServer sshServer, SshProperties sshProperties){
        sshServer.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File(sshProperties.getHostKeyLocation()).toPath()));
    }

    private static void configureSubsystem(SshServer sshServer, SshProperties sshProperties){

        String pathName = new File(sshProperties.getSharedLocation()).getAbsolutePath();
        if(OsUtils.isWin32()){
            pathName = pathName.replaceAll("\\\\\\.\\\\", "/");
        }else{
            pathName = pathName.replaceAll("/\\./","/");
        }
        sshServer.setFileSystemFactory(new VirtualFileSystemFactory(Paths.get(pathName)));
        sshServer.setCommandFactory(new ScpCommandFactory());
        sshServer.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));

    }

}
